unit QImport2;

{$IFDEF WIN32}
  {$R QIResStr.res}
  {$R QIEULA.res}
{$ENDIF}

{$I VerCtrl.inc}

{$IFDEF VCL6}
  {$WARN UNIT_PLATFORM OFF}
{$ENDIF}

interface

uses Classes, DB, SysUtils, IniFiles
     {$IFDEF WIN32}{$IFNDEF NOGUI}, DBGrids, ComCtrls, Grids{$ENDIF}{$ENDIF}
     {$IFDEF LINUX}{$IFNDEF NOGUI}, QDBGrids, QComCtrls, QGrids{$ENDIF}{$ENDIF};

type
  TQImport2 = class;
  TAllowedImport = (aiXLS, aiDBF, aiXML, aiTXT, aiCSV);
  TAllowedImports = set of TAllowedImport;

  TQuoteAction = (qaNone, qaAdd, qaRemove);
  TQImportCharCase = (iccNone, iccUpper, iccLower, iccUpperFirst, iccUpperFirstWord);
  TQImportCharSet = (icsNone, icsAnsi, icsOem);

{$IFDEF WIN32}
  TLocalizeEvent = procedure(StringID: Integer; var ResultString: string) of object;

  TQImportLocale = class(TObject)
  private
    FDllHandle: Cardinal;
    FLoaded: Boolean;
    FOnLocalize: TLocalizeEvent;
    FIDEMode: Boolean;
  public
    constructor Create;
    function LoadStr(ID: Integer): string;
    procedure LoadDll(const Name: string);
    procedure UnloadDll;
    property OnLocalize: TLocalizeEvent read FOnLocalize write FOnLocalize;
  end;
{$ENDIF}  

  TQImportFormats = class(TPersistent)
  private
    FDecimalSeparator: char;
    FThousandSeparator: char;
    FShortDateFormat: string;
    FLongDateFormat: string;
    FDateSeparator: char;
    FShortTimeFormat: string;
    FLongTimeFormat: string;
    FTimeSeparator: char;
    FBooleanTrue: TStrings;
    FBooleanFalse: TStrings;
    FNullValues: TStrings;

    FOldDecimalSeparator: char;
    FOldThousandSeparator: char;
    FOldShortDateFormat: string;
    FOldLongDateFormat: string;
    FOldDateSeparator: char;
    FOldShortTimeFormat: string;
    FOldLongTimeFormat: string;
    FOldTimeSeparator: char;

    function IsDecimalSeparator: boolean;
    function IsThousandSeparator: boolean;
    function IsShortDateFormat: boolean;
    function IsLongDateFormat: boolean;
    function IsDateSeparator: boolean;
    function IsShortTimeFormat: boolean;
    function IsLongTimeFormat: boolean;
    function IsTimeSeparator: boolean;

    procedure SetBooleanTrue(const Value: TStrings);
    procedure SetBooleanFalse(const Value: TStrings);
    procedure SetNullValues(const Value: TStrings);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    procedure StoreFormats;
    procedure RestoreFormats;
    procedure ApplyParams;
  published
    property DecimalSeparator: char read FDecimalSeparator
      write FDecimalSeparator stored IsDecimalSeparator;
    property ThousandSeparator: char read FThousandSeparator
      write FThousandSeparator stored IsThousandSeparator;
    property ShortDateFormat: string read FShortDateFormat
      write FShortDateFormat stored IsShortDateFormat;
    property LongDateFormat: string read FLongDateFormat
      write FLongDateFormat stored IsLongDateFormat;
    property DateSeparator: char read FDateSeparator write FDateSeparator
      stored IsDateSeparator;
    property ShortTimeFormat: string read FShortTimeFormat
      write FShortTimeFormat stored IsShortTimeFormat;
    property LongTimeFormat: string read FLongTimeFormat
      write FLongTimeFormat stored IsLongTimeFormat;
    property TimeSeparator: char read FTimeSeparator write FTimeSeparator
      stored IsTimeSeparator;
    property BooleanTrue: TStrings read FBooleanTrue write SetBooleanTrue;
    property BooleanFalse: TStrings read FBooleanFalse write SetBooleanFalse;
    property NullValues: TStrings read FNullValues write SetNullValues;
  end;

  TQImportReplacement = class(TCollectionItem)
  private
    FTextToFind: string;
    FReplaceWith: string;
    FIgnoreCase: boolean;
  protected
    function  GetDisplayName: string; override;
  public
    constructor Create(Collection: TCollection); override;
    procedure Assign(Source: TPersistent); override;
  published
    property TextToFind: string read FTextToFind write FTextToFind;
    property ReplaceWith: string read FReplaceWith write FReplaceWith;
    property IgnoreCase: boolean read FIgnoreCase
      write FIgnoreCase default false;
  end;

  TQImportReplacements = class(TCollection)
  private
    FHolder: TPersistent;
    function GetItem(Index: integer): TQImportReplacement;
    procedure SetItem(Index: integer; Replacement: TQImportReplacement);
  protected
    function GetOwner: TPersistent; override;
  public
    property Holder: TPersistent read FHolder;
    constructor Create(Holder: TPersistent);
    function Add: TQImportReplacement;
    property Items[Index: integer]: TQImportReplacement read GetItem
      write SetItem; default;
    function ItemExists(const ATextToFind, AReplaceWith: string;
      AIgnoreCase: Boolean): Boolean;
  end;

  TQImportFieldFormat = class(TCollectionItem)
  private
    FFieldName: string;
    FGeneratorValue: integer;
    FGeneratorStep: integer;
    FConstantValue: string;
    FNullValue: string;
    FDefaultValue: string;
    FLeftQuote: string;
    FRightQuote: string;
    FQuoteAction: TQuoteAction;
    FCharCase: TQImportCharCase;
    FCharSet: TQImportCharSet;
    FReplacements: TQImportReplacements;

    function IsConstant: boolean;
    function IsNull: boolean;
    function IsDefault: boolean;
    function IsLeftQuote: boolean;
    function IsRightQuote: boolean;

    procedure SetReplacements(const Value: TQImportReplacements);
  protected
    function  GetDisplayName: string; override;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    function IsDefaultValues: boolean;
  published
    property FieldName: string read FFieldName write FFieldName;
    property GeneratorValue: integer read FGeneratorValue
      write FGeneratorValue default 0;
    property GeneratorStep: integer read FGeneratorStep
      write FGeneratorStep default 0;
    property ConstantValue: string read FConstantValue
      write FConstantValue stored IsConstant;
    property NullValue: string read FNullValue
      write FNullValue stored IsNull;
    property DefaultValue: string read FDefaultValue
      write FDefaultValue stored IsDefault;
    property LeftQuote: string read FLeftQuote
      write FLeftQuote stored IsLeftQuote;
    property RightQuote: string read FRightQuote
      write FRightQuote stored IsRightQuote;
    property QuoteAction: TQuoteAction read FQuoteAction
      write FQuoteAction default qaNone;
    property CharCase: TQImportCharCase read FCharCase
      write FCharCase default iccNone;
    property CharSet: TQImportCharSet read FCharSet
      write FCharSet default icsNone;
    property Replacements: TQImportReplacements read FReplacements
      write SetReplacements;
  end;

  TQImportFieldFormats = class(TCollection)
  private
    FHolder: TComponent;
    function GetItem(Index: integer): TQImportFieldFormat;
    procedure SetItem(Index: integer; FieldFormat: TQImportFieldFormat);
  protected
    function GetOwner: TPersistent; override;
  public
    property Holder: TComponent read FHolder;
    constructor Create(AHolder: TComponent);
    function Add: TQImportFieldFormat;
    function IndexByName(const FieldName: string): integer;
    property Items[Index: integer]: TQImportFieldFormat read GetItem write SetItem; default;
  end;

  TQImportCol = class
  private
    FValue: string;
    FName: string;
    FIsBinary: boolean;
    FColumnIndex: integer;
  public
    constructor Create;

    property Name: string read FName write FName;
    property Value: string read FValue write FValue;
    property IsBinary: boolean read FIsBinary;
  end;

  TQImportRow = class(TList)
  private
    FIndex: TStringList;
    FQImport: TQImport2;

    function Get(Index: Integer): TQImportCol;
    procedure Put(Index: Integer; const Value: TQImportCol);
  public
    constructor Create(AImport: TQImport2);
    destructor Destroy; override;
    function Add(const AName: string): TQImportCol;
    procedure Clear; {$IFNDEF VCL3}override;{$ENDIF}
    procedure Delete(Index: integer);
    function First: TQImportCol;
    procedure Insert(Index: Integer; Item: TQImportCol);
    procedure SetValue(const AName, AValue: string; IsBinary: boolean);
    procedure ClearValues;
    function Last: TQImportCol;
    function IndexOf(Item: TQImportCol): integer;
    function Remove(Item: TQImportCol): integer;
    function ColByName(const AName: string): TQImportCol;

    property QImport: TQImport2 read FQImport;
    property Items[Index: Integer]: TQImportCol read Get write Put; default;
  end;

  TQImportGenerator = class
  private
    FName: string;
    FValue: integer;
    FStep: integer;
  public
    function GetNewValue: integer;
    property Name: string read FName write FName;
    property Value: integer read FValue write FValue;
    property Step: integer read FStep write FStep;
  end;

  TQImportGenerators = class(TList)
  private
    function Get(Index: Integer): TQImportGenerator;
    procedure Put(Index: Integer; const Value: TQImportGenerator);
  public
    destructor Destroy; override;
    function Add(const AName: string; AValue, AStep: integer): TQImportGenerator;
    procedure Delete(Index: integer);
    function GetNewValue(const AName: string): integer;
    function GenByName(const AName: string): TQImportGenerator;

    property Items[Index: Integer]: TQImportGenerator read Get write Put; default;
  end;

  TQImportFieldType = (iftUnknown, iftString, iftInteger, iftBoolean,
    iftDouble, iftCurrency, iftDateTime);
  EQImportError = class(Exception);
  TQImportAddType = (qatAppend, qatInsert);
  TQImportDestination = (qidDataSet, qidDBGrid, qidListView, qidStringGrid,
    qidUserDefined);
  TQImportResult = (qirOk, qirContinue, qirBreak);
  TQImportMode = (qimInsertAll, qimInsertNew, qimUpdate, qimUpdateOrInsert,
    qimDelete, qimDeleteOrInsert);
  TQImportAction = (qiaNone, qiaInsert, qiaUpdate, qiaDelete);

  TImportCancelEvent = procedure(Sender: TObject;
    var Continue: boolean) of object;
  TImportBeforePostEvent = procedure(Sender: TObject;
    Row: TQImportRow; var Accept: boolean) of object;
  TUserDefinedImportEvent = procedure(Sender: TObject;
    Row: TQImportRow) of object;
  TImportAfterPostEvent = procedure(Sender: TObject;
    Row: TQImportRow) of object;
  TImportLoadTemplateEvent = procedure(Sender: TObject;
    const FileName: string) of object;
//  TImportSQLIdentifierEvent = procedure (Sender: TObject;
//    var Identifier: string) of object;
  TDestinationLocateEvent = procedure(Sender: TObject; KeyColumns: TStrings;
    Row: TQImportRow; var KeyFields: string; var KeyValues: Variant) of object;
  
  TQImport2 = class(TComponent)
  private
    FDataSet: TDataSet;
    {$IFNDEF NOGUI}
    FDBGrid: TDBGrid;
    FListView: TListView;
    FStringGrid: TStringGrid;
    FGridCaptionRow: integer;
    FGridStartRow: integer;
    {$ENDIF}

    FFileName: string;
    FErrors: TStrings;
    FMap: TStrings;
    FImportRecCount: integer;
    FCommitRecCount: integer;
    FCommitAfterDone: boolean;
    FErrorLog: boolean;
    FErrorLogFileName: string;
    FRewriteErrorLogFile: boolean;
    FShowErrorLog: boolean;
    FErrorLogFS: TFileStream;
//    FSQLLog: boolean;
//    FSQLLogFileName: string;
//    FSQLLogFileRewrite: boolean;
//    FSQL: TFileStream;
    FSkipFirstRows: integer;
    FSkipFirstCols: integer;
    FImportedRecs: integer;
    FCanceled: boolean;
    FFormats: TQImportFormats;
    FFieldFormats: TQImportFieldFormats;
    FAddType: TQImportAddType;
    FImportDestination: TQImportDestination;
    FImportMode: TQImportMode;
    FKeyColumns: TStrings;
    FCurrentLineNumber: Integer;

    FIsCSV: boolean;
    FLastAction: TQImportAction;

    FStream: TStream;
    FComma: char;
    FQuote: char;

    FOnBeforeImport: TNotifyEvent;
    FOnAfterImport: TNotifyEvent;
    FOnImportRecord: TNotifyEvent;
    FOnImportError: TNotifyEvent;
    FOnImportErrorAdv: TNotifyEvent;
    FOnNeedCommit: TNotifyEvent;
    FOnImportCancel: TImportCancelEvent;
    FOnBeforePost: TImportBeforePostEvent;
    FOnAfterPost: TImportAfterPostEvent;
//    FOnGetSQLIdentifier: TImportSQLIdentifierEvent;
    FOnUserDefinedImport: TUserDefinedImportEvent;
    FOnImportRowComplete: TUserDefinedImportEvent;
    FOnDestinationLocate: TDestinationLocateEvent;

    FAbout: string;
    FVersion: string;
    FMappedColumns: TStrings;
    FAllowDuplicates: Boolean;

    procedure SetDataSet(const Value: TDataSet);
    {$IFNDEF NOGUI}
    procedure SetDBGrid(const Value: TDBGrid);
    procedure SetListView(const Value: TListView);
    procedure SetStringGrid(const Value: TStringGrid);
    {$ENDIF}
    procedure SetKeyColumns(const Value: TStrings);

    procedure SetFileName(const Value: string);
    procedure SetMap(const Value: TStrings);
    function GetErrorRecs: integer;
    procedure SetFormats(const Value: TQImportFormats);
    procedure SetFieldFormats(const Value: TQImportFieldFormats);
  private
    {$IFNDEF NOGUI}
    FCurrListItem: TListItem;
    FCurrStrGrRow: integer;
    {$ENDIF}
    procedure InitializeImportRow;
  protected
    FTotalRecCount: integer;
    FImportRow: TQImportRow;
    FImportGenerators: TQImportGenerators;

    property IsCSV: boolean read FIsCSV;

    procedure Notification(AComponent: TComponent;
      Operation: TOperation); override;

    procedure DoImport;

    procedure BeforeImport; virtual;
    procedure StartImport; virtual; abstract;
    function CheckCondition: boolean; virtual; abstract;
    function Skip: boolean; virtual; abstract;
    procedure FillImportRow; virtual; abstract;
    function ImportData: TQImportResult; virtual; abstract;
    procedure DataManipulation;
    procedure ChangeCondition; virtual; abstract;
    procedure FinishImport; virtual; abstract;
    procedure AfterImport; virtual;

    procedure DoAfterSetFileName; virtual;
    function CheckProperties: boolean; virtual;
    procedure DoUserDataFormat(Col: TQImportCol);
    function CanContinue: boolean;
    function StringToField(Field: TField; const Str: string; IsBinary: boolean): string;

    procedure DoLoadConfiguration(IniFile: TIniFile); virtual;
    procedure DoSaveConfiguration(IniFile: TIniFile); virtual;

    property SkipFirstRows: integer read FSkipFirstRows write FSkipFirstRows;
    property SkipFirstCols: integer read FSkipFirstCols write FSkipFirstCols;
  protected
    procedure DestinationInsert;
    procedure DestinationEdit;
    procedure DestinationDelete;
    procedure DestinationSetValues;
    procedure DestinationPost;
    procedure DestinationCancel;
    function  DestinationFindColumn(const ColName: string): integer;
    procedure DestinationDisableControls;
    procedure DestinationEnableControls;
    procedure CheckDestination;
    function DestinationFindByKey: boolean;
    function DestinationFindByFields: Boolean;
    function DestinationColCount: integer;
    function DestinationColName(Index: integer): string;

    procedure DoBeginImport; dynamic;
    function  DoBeforePost: boolean; dynamic;
    procedure DoAfterPost; dynamic;
    procedure DoImportRecord; dynamic;
    procedure DoImportError(Error: Exception); dynamic;
    procedure WriteErrorLog(const ErrorMsg: string);
    procedure DoNeedCommit; dynamic;
    procedure DoEndImport; dynamic;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function Execute: boolean;
    procedure ImportToCSV(Stream: TStream; Comma, Quote: char);
    procedure Cancel;
    procedure LoadConfiguration(const FileName: string);
    procedure SaveConfiguration(const FileName: string);
    property Errors: TStrings read FErrors;
    property ImportedRecs: integer read FImportedRecs;
    property ErrorRecs: integer read GetErrorRecs;
    property Canceled: boolean read FCanceled;
    property LastAction: TQImportAction read FLastAction;
    property TotalRecCount: integer read FTotalRecCount;
    property FileName: string read FFileName write SetFileName;
    property CurrentLineNumber: Integer read FCurrentLineNumber;
  published
    property About: string read FAbout write FAbout;
    property Version: string read FVersion write FVersion;

    property DataSet: TDataSet read FDataSet write SetDataSet;
    {$IFNDEF NOGUI}
    property DBGrid: TDBGrid read FDBGrid write SetDBGrid;
    property ListView: TListView read FListView write SetListView;
    property StringGrid: TStringGrid read FStringGrid write SetStringGrid;
    property GridCaptionRow: integer read FGridCaptionRow
      write FGridCaptionRow default -1;
    property GridStartRow: integer read FGridStartRow
      write FGridStartRow default -1;
    {$ENDIF}

    property ImportDestination: TQImportDestination read FImportDestination
      write FImportDestination default qidDataSet;
    property ImportMode: TQImportMode read FImportMode
      write FImportMode default qimInsertAll;

    property Map: TStrings read FMap write SetMap;
    property Formats: TQImportFormats read FFormats write SetFormats;
    property FieldFormats: TQImportFieldFormats read FFieldFormats
      write SetFieldFormats;
    property ErrorLog: boolean read FErrorLog write FErrorLog default false;
    property ErrorLogFileName: string read FErrorLogFileName
      write FErrorLogFileName;
    property RewriteErrorLogFile: boolean read FRewriteErrorLogFile
      write FRewriteErrorLogFile default true;
    property ShowErrorLog: boolean read FShowErrorLog
      write FShowErrorLog default false;
//    property SQLLog: boolean read FSQLLog write FSQLLog default false;
//    property SQLLogFileName: string read FSQLLogFileName
//      write FSQLLogFileName;
//    property SQLLogFileRewrite: boolean read FSQLLogFileRewrite
//      write FSQLLogFileRewrite default true;
    property ImportRecCount: integer read FImportRecCount
      write FImportRecCount default 0;
    property CommitRecCount: integer read FCommitRecCount
      write FCommitRecCount default 100;
    property CommitAfterDone: boolean read FCommitAfterDone
      write FCommitAfterDone default true;
    property AddType: TQImportAddType read FAddType
      write FAddType default qatAppend;
    property KeyColumns: TStrings read FKeyColumns write SetKeyColumns;
    property AllowDuplicates: Boolean read FAllowDuplicates
      write FAllowDuplicates default True;

    property OnBeforeImport: TNotifyEvent read FOnBeforeImport
      write FOnBeforeImport;
    property OnAfterImport: TNotifyEvent read FOnAfterImport
      write FOnAfterImport;
    property OnImportRecord: TNotifyEvent read FOnImportRecord
      write FOnImportRecord;
    property OnImportError: TNotifyEvent read FOnImportError
      write FOnImportError;
    property OnImportErrorAdv: TNotifyEvent read FOnImportErrorAdv
      write FOnImportErrorAdv;
    property OnNeedCommit: TNotifyEvent read FOnNeedCommit
      write FOnNeedCommit;
    property OnImportCancel: TImportCancelEvent read FOnImportCancel
      write FOnImportCancel;
    property OnBeforePost: TImportBeforePostEvent read FOnBeforePost
      write FOnBeforePost;
    property OnAfterPost: TImportAfterPostEvent read FOnAfterPost
      write FOnafterPost;
    property OnUserDefinedImport: TUserDefinedImportEvent
      read FOnUserDefinedImport write FOnUserDefinedImport;
    property OnImportRowComplete: TUserDefinedImportEvent
      read FOnImportRowComplete write FOnImportRowComplete;
//    property OnGetSQLIdentifier: TImportSQLIdentifierEvent
//      read FOnGetSQLIdentifier write FOnGetSQLIdentifier;
    property OnDestinationLocate: TDestinationLocateEvent read
      FOnDestinationLocate write FOnDestinationLocate;
  end;

function GetImportFieldType(Field: TField): TQImportFieldType;
procedure StrReplace(var S: AnsiString; const Search, Replace: AnsiString;
  Flags: TReplaceFlags);

{$IFDEF WIN32}
function QImportLocale: TQImportLocale;
function QImportLoadStr(ID: Integer): string;
{$ENDIF}

implementation

uses QImport2Common
     {$IFDEF WIN32}, QImport2StrIDs, Windows, FileCtrl, ShellAPI
       {$IFDEF TRIAL}, fuQImport2About{$ENDIF}
       {$IFDEF VCL6}, Variants{$ENDIF}
     {$ENDIF}
     {$IFDEF LINUX}, QImport2Consts, Variants{$ENDIF};

const
  LF = {$IFDEF WIN32}#13#10{$ENDIF}{$IFDEF LINUX}#10{$ENDIF};

{$IFDEF WIN32}
var
  Locale: TQImportLocale = nil;
{$ENDIF}

{$IFDEF TRIAL}
{$IFDEF WIN32}
function IsIDERuning: Boolean;
begin
  Result := (FindWindow('TAppBuilder', nil) <> 0) or
            (FindWindow('TPropertyInspector', nil) <> 0) or
            (FindWindow('TAlignPalette', nil) <> 0);
end;
{$ENDIF}
{$ENDIF}

function GetImportFieldType(Field: TField): TQImportFieldType;
begin
  case Field.DataType of
    ftBlob,
    ftMemo,
    //igorp
    ftOraClob,
    ftOraBlob,
    //\igorp
    {$IFNDEF VCL3}
    ftWideString,
    {$ENDIF}
    ftString,
    ftGUID: Result := iftString;
    ftSmallint,
    ftInteger,
    {ab}ftAutoInc,{/ab}
{$IFNDEF VCL3}
    ftLargeInt,
{$ENDIF}
    ftWord: Result := iftInteger;
    ftBoolean: Result := iftBoolean;
    ftFloat,
    ftBCD
{$IFDEF VCL6}
    , ftFMTBcd
{$ENDIF}
    : Result := iftDouble;
    ftCurrency: Result := iftCurrency;
    ftDate,
    ftTime,
    ftDateTime
{$IFDEF VCL6}
    , ftTimeStamp
{$ENDIF}
    :Result := iftDateTime;
  else
    Result := iftUnknown;
  end;
end;

procedure StrReplaceCS(var S: AnsiString; const Search, Replace: AnsiString; Flags: TReplaceFlags);
var
  ResultStr: string;
  SourcePtr: PChar;
  SourceMatchPtr: PChar;
  SearchMatchPtr: PChar;
  ResultPtr: PChar;
  SearchLength,
  ReplaceLength,
  ResultLength: Integer;
  C: Char;
begin
  SearchLength := Length(Search);
  ReplaceLength := Length(Replace);

  if Length(Search) >= ReplaceLength then
    ResultLength := Length(S)
  else
    ResultLength := ((Length(S) div Length(Search)) + 1) * Length(Replace);
  SetLength(ResultStr, ResultLength);

  ResultPtr := PChar(ResultStr);
  SourcePtr := PChar(S);
  C := Search[1];

  while True do
  begin
    while (SourcePtr^ <> C) and (SourcePtr^ <> #0) do
    begin
      ResultPtr^ := SourcePtr^;
      Inc(ResultPtr);
      Inc(SourcePtr);
    end;

    if SourcePtr^ = #0 then
      Break
    else
    begin

      SourceMatchPtr := SourcePtr + 1;
      SearchMatchPtr := PChar(Search) + 1;
      while (SourceMatchPtr^ = SearchMatchPtr^) and (SearchMatchPtr^ <> #0) do
      begin
        Inc(SourceMatchPtr);
        Inc(SearchMatchPtr);
      end;

      if SearchMatchPtr^ = #0 then
      begin
        Move((@Replace[1])^, ResultPtr^, ReplaceLength);
        Inc(SourcePtr, SearchLength);
        Inc(ResultPtr, ReplaceLength);

        if not (rfReplaceAll in Flags) then
        begin
          while SourcePtr^ <> #0 do
          begin
            ResultPtr^ := SourcePtr^;
            Inc(ResultPtr);
            Inc(SourcePtr);
          end;
          Break;
        end;
      end
      else
      begin
        ResultPtr^ := SourcePtr^;
        Inc(ResultPtr);
        Inc(SourcePtr);
      end;
    end;
  end;

  ResultPtr^ := #0;
  S := ResultStr;
  SetLength(S, StrLen(PChar(S)));
end;

procedure StrReplaceCI(var S: AnsiString; Search, Replace: AnsiString; Flags: TReplaceFlags);
var
  ResultStr: string;
  SourcePtr: PChar;
  SourceMatchPtr: PChar;
  SearchMatchPtr: PChar;
  ResultPtr: PChar;
  SearchLength,
  ReplaceLength,
  ResultLength: Integer;
  C: Char;
begin
  Search := UpperCase(Search);
  SearchLength := Length(Search);
  ReplaceLength := Length(Replace);

  if Length(Search) >= ReplaceLength then
    ResultLength := Length(S)
  else
    ResultLength := ((Length(S) div Length(Search)) + 1) * Length(Replace);
  SetLength(ResultStr, ResultLength);

  ResultPtr := PChar(ResultStr);
  SourcePtr := PChar(S);
  C := Search[1];

  while True do
  begin
    while (UpCase(SourcePtr^) <> C) and (SourcePtr^ <> #0) do
    begin
      ResultPtr^ := SourcePtr^;
      Inc(ResultPtr);
      Inc(SourcePtr);
    end;

    if SourcePtr^ = #0 then
      Break
    else
    begin
      SourceMatchPtr := SourcePtr + 1;
      SearchMatchPtr := PChar(Search) + 1;
      while (UpCase(SourceMatchPtr^) = SearchMatchPtr^) and (SearchMatchPtr^ <> #0) do
      begin
        Inc(SourceMatchPtr);
        Inc(SearchMatchPtr);
      end;

      if SearchMatchPtr^ = #0 then
      begin
        Move((@Replace[1])^, ResultPtr^, ReplaceLength);
        Inc(SourcePtr, SearchLength);
        Inc(ResultPtr, ReplaceLength);

        if not (rfReplaceAll in Flags) then
        begin
          while SourcePtr^ <> #0 do
          begin
            ResultPtr^ := SourcePtr^;
            Inc(ResultPtr);
            Inc(SourcePtr);
          end;
          Break;
        end;
      end
      else
      begin
        ResultPtr^ := SourcePtr^;
        Inc(ResultPtr);
        Inc(SourcePtr);
      end;
    end;
  end;
  
  ResultPtr^ := #0;
  S := ResultStr;
  SetLength(S, StrLen(PChar(S)));
end;

procedure StrReplace(var S: AnsiString; const Search, Replace: AnsiString;
  Flags: TReplaceFlags);
begin
  if (S <> '') and (Search <> '') then
  begin
    if rfIgnoreCase in Flags then
      StrReplaceCI(S, Search, Replace, Flags)
    else
      StrReplaceCS(S, Search, Replace, Flags);
  end;
end;

{$IFDEF WIN32}
function QImportLocale: TQImportLocale;
begin
  if Locale = nil then
    Locale := TQImportLocale.Create;
  Result := Locale;
end;

function QImportLoadStr(ID: Integer): string;
begin
  Result := QImportLocale.LoadStr(ID);
end;
{$ENDIF}

{ TQImportFormats }

constructor TQImportFormats.Create;
begin
  inherited;
  FDecimalSeparator := SysUtils.DecimalSeparator;
  FThousandSeparator := SysUtils.ThousandSeparator;
  FShortDateFormat := SysUtils.ShortDateFormat;
  FLongDateFormat := SysUtils.LongDateFormat;
  FDateSeparator := SysUtils.DateSeparator;
  FShortTimeFormat := SysUtils.ShortTimeFormat;
  FLongTimeFormat := SysUtils.LongTimeFormat;
  FTimeSeparator := SysUtils.TimeSeparator;
  FBooleanTrue := TStringList.Create;
  FBooleanTrue.Add({$IFDEF WIN32}QImportLoadStr(QID_BooleanTrue){$ENDIF}
                   {$IFDEF LINUX}QID_BooleanTrue{$ENDIF});
  FBooleanFalse := TStringList.Create;
  FBooleanFalse.Add({$IFDEF WIN32}QImportLoadStr(QID_BooleanFalse){$ENDIF}
                    {$IFDEF LINUX}QID_BooleanFalse{$ENDIF});
  FNullValues := TStringList.Create;
  FNullValues.Add({$IFDEF WIN32}QImportLoadStr(QID_NullValue){$ENDIF}
                  {$IFDEF LINUX}QID_NullValue{$ENDIF});
end;

destructor TQImportFormats.Destroy;
begin
  FBooleanTrue.Free;
  FBooleanFalse.Free;
  FNullValues.Free;
  inherited;
end;

procedure TQImportFormats.Assign(Source: TPersistent);
begin
  if Source is TQImportFormats then begin
    DecimalSeparator := (Source as TQImportFormats).DecimalSeparator;
    ThousandSeparator := (Source as TQImportFormats).ThousandSeparator;
    ShortDateFormat := (Source as TQImportFormats).ShortDateFormat;
    LongDateFormat := (Source as TQImportFormats).LongDateFormat;
    DateSeparator := (Source as TQImportFormats).DateSeparator;
    ShortTimeFormat := (Source as TQImportFormats).ShortTimeFormat;
    LongTimeFormat := (Source as TQImportFormats).LongTimeFormat;
    TimeSeparator := (Source as TQImportFormats).TimeSeparator;
    BooleanTrue := (Source as TQImportFormats).BooleanTrue;
    BooleanFalse := (Source as TQImportFormats).BooleanFalse;
    NullValues := (Source as TQImportFormats).NullValues;
    Exit;
  end;
  inherited Assign(Source);
end;

procedure TQImportFormats.StoreFormats;
begin
  FOldDecimalSeparator := SysUtils.DecimalSeparator;
  FOldThousandSeparator := SysUtils.ThousandSeparator;
  FOldShortDateFormat := SysUtils.ShortDateFormat;
  FOldLongDateFormat := SysUtils.LongDateFormat;
  FOldDateSeparator := SysUtils.DateSeparator;
  FOldShortTimeFormat := SysUtils.ShortTimeFormat;
  FOldLongTimeFormat := SysUtils.LongTimeFormat;
  FOldTimeSeparator := SysUtils.TimeSeparator;
end;

procedure TQImportFormats.RestoreFormats;
begin
  SysUtils.DecimalSeparator := FOldDecimalSeparator;
  SysUtils.ThousandSeparator := FOldThousandSeparator;
  SysUtils.ShortDateFormat := FOldShortDateFormat;
  SysUtils.LongDateFormat := FOldLongDateFormat;
  SysUtils.DateSeparator := FOldDateSeparator;
  SysUtils.ShortTimeFormat := FOldShortTimeFormat;
  SysUtils.LongTimeFormat := FOldLongTimeFormat;
  SysUtils.TimeSeparator := FOldTimeSeparator;
end;

procedure TQImportFormats.ApplyParams;
begin
  SysUtils.DecimalSeparator := FDecimalSeparator;
  SysUtils.ThousandSeparator := FThousandSeparator;
  SysUtils.ShortDateFormat := FShortDateFormat;
  SysUtils.LongDateFormat := FLongDateFormat;
  SysUtils.DateSeparator := FDateSeparator;
  SysUtils.ShortTimeFormat := FShortTimeFormat;
  SysUtils.LongTimeFormat := FLongTimeFormat;
  SysUtils.TimeSeparator := FTimeSeparator;
end;

function TQImportFormats.IsDecimalSeparator: boolean;
begin
  Result := FDecimalSeparator <> SysUtils.DecimalSeparator;
end;

function TQImportFormats.IsThousandSeparator: boolean;
begin
  Result := FThousandSeparator <> SysUtils.ThousandSeparator;
end;

function TQImportFormats.IsShortDateFormat: Boolean;
begin
  Result := FShortDateFormat <> SysUtils.ShortDateFormat;
end;

function TQImportFormats.IsLongDateFormat: Boolean;
begin
  Result := FLongDateFormat <> SysUtils.LongDateFormat;
end;

function TQImportFormats.IsDateSeparator: Boolean;
begin
  Result := FDateSeparator <> SysUtils.DateSeparator;
end;

function TQImportFormats.IsShortTimeFormat: Boolean;
begin
  Result := FShortTimeFormat <> SysUtils.ShortTimeFormat;
end;

function TQImportFormats.IsLongTimeFormat: Boolean;
begin
  Result := FLongTimeFormat <> SysUtils.LongTimeFormat;
end;

function TQImportFormats.IsTimeSeparator: Boolean;
begin
  Result := FTimeSeparator <> SysUtils.TimeSeparator;
end;

procedure TQImportFormats.SetBooleanTrue(const Value: TStrings);
begin
  FBooleanTrue.Assign(Value);
end;

procedure TQImportFormats.SetBooleanFalse(const Value: TStrings);
begin
  FBooleanFalse.Assign(Value);
end;

procedure TQImportFormats.SetNullValues(const Value: TStrings);
begin
  FNullValues.Assign(Value);
end;

{ TQImport2 }

constructor TQImport2.Create(AOwner: TComponent);
begin
  inherited;
  {$IFNDEF NOGUI}
  FGridCaptionRow := -1;
  FGridStartRow := -1;
  {$ENDIF}

  FErrors := TStringList.Create;
  FMap := TStringList.Create;
  FFormats := TQImportFormats.Create;
  FFieldFormats := TQImportFieldFormats.Create(Self);
  FImportRow := TQImportRow.Create(Self);
  FImportGenerators := TQImportGenerators.Create;

  FImportRecCount := 0;
  FCommitRecCount := 100;
  FCommitAfterDone := true;
  FSkipFirstRows := 0;
  FSkipFirstCols := 0;
  FImportedRecs := 0;
  FCanceled := false;
  FErrorLog := false;
  FErrorLogFileName:= 'error.log';
  FRewriteErrorLogFile := true;
  FShowErrorLog := false;
//  FSQLLog := false;
//  FSQLLogFileRewrite := true;
//  FSQL := nil;
  FTotalRecCount := 0;
  FAddType := qatInsert;
  FImportDestination := qidDataSet;
  FImportMode := qimInsertAll;
  FKeyColumns := TStringList.Create;
  FMappedColumns := TStringList.Create;

  FIsCSV := false;
  FLastAction := qiaNone;

  FCurrentLineNumber := 1;
  FAllowDuplicates := True;
end;

destructor TQImport2.Destroy;
begin
  FImportGenerators.Free;
  FImportRow.Free;
  FFieldFormats.Free;
  FFormats.Free;
  FMap.Free;
  FErrors.Free;
  FKeyColumns.Free;
  FMappedColumns.Free;
  inherited;
end;

function TQImport2.Execute: boolean;
begin
  {$IFDEF TRIAL}
  {$IFDEF WIN32}
    if not IsIDERuning then
      ShowAboutForm;
  {$ENDIF}
  {$ENDIF}
  Result := CheckProperties;

  BeforeImport;
  try
    DoBeginImport;
    DoImport;
  finally
    try
      DoEndImport
    finally
      AfterImport;
    end;
  end;
end;

procedure TQImport2.ImportToCSV(Stream: TStream; Comma, Quote: char);
begin
  {$IFDEF TRIAL}
  {$IFDEF WIN32}
    if not IsIDERuning then
      ShowAboutForm;
  {$ENDIF}
  {$ENDIF}
  FIsCSV := true;
  try
    CheckProperties;
    if not Assigned(Stream) then
      raise Exception.Create('Stream must be assigned');
    FStream := Stream;
    FComma := Comma;
    FQuote := Quote;
    BeforeImport;
    DoBeginImport;
    DoImport;
  finally
    try
      DoEndImport;
      AfterImport;
    finally
      FIsCSV := false;
    end;
  end;
end;

procedure TQImport2.Cancel;
begin
  FCanceled := true;
end;

procedure TQImport2.LoadConfiguration(const FileName: string);
var
  FIniFile: TIniFile;
begin
  if not FileExists(FileName) then Exit;
  FIniFile := TIniFile.Create(FileName);
  try
    DoLoadConfiguration(FIniFile);
  finally
    FIniFile.Free;
  end;
end;

procedure TQImport2.SaveConfiguration(const FileName: string);
var
  FIniFile: TIniFile;
begin
  if Trim(FileName) = EmptyStr then Exit;
  FIniFile := TIniFile.Create(FileName);
  try
    DoSaveConfiguration(FIniFile);
  finally
    FIniFile.Free;
  end;
end;

procedure TQImport2.DoLoadConfiguration(IniFile: TIniFile);
var
  i: integer;
  AStrings: TStrings;
  str: string;
begin
  with IniFile do
  begin
    AStrings := TStringList.Create;
    try
      Self.FileName := ReadString(QI_BASE, QI_FILE_NAME, Self.FileName);
      AStrings.Clear;
      Map.Clear;
      ReadSection(QI_MAP, AStrings);
      for i := 0 to AStrings.Count - 1 do
        Map.Values[AStrings[i]] := ReadString(QI_MAP, AStrings[i], EmptyStr);
      Formats.DecimalSeparator := ReadString(BASE_FORMATS, BF_DECIMAL_SEPARATOR,
        Formats.DecimalSeparator)[1];
      Formats.ThousandSeparator := ReadString(BASE_FORMATS,
        BF_THOUSAND_SEPARATOR, Formats.ThousandSeparator)[1];
      Formats.ShortDateFormat := ReadString(BASE_FORMATS, BF_SHORT_DATE_FORMAT,
        Formats.ShortDateFormat);
      Formats.LongDateFormat := ReadString(BASE_FORMATS, BF_LONG_DATE_FORMAT,
        Formats.LongDateFormat);
      Formats.ShortTimeFormat := ReadString(BASE_FORMATS, BF_SHORT_TIME_FORMAT,
        Formats.ShortTimeFormat);
      Formats.LongTimeFormat := ReadString(BASE_FORMATS, BF_LONG_TIME_FORMAT,
        Formats.LongTimeFormat);
      AStrings.Clear;
      Formats.BooleanTrue.Clear;
      ReadSection(BOOLEAN_TRUE, AStrings);
      for i := 0 to AStrings.Count - 1 do
        Formats.BooleanTrue.Add(AStrings[i]);
      AStrings.Clear;
      Formats.BooleanFalse.Clear;
      ReadSection(BOOLEAN_FALSE, AStrings);
      for i := 0 to AStrings.Count - 1 do
        Formats.BooleanFalse.Add(AStrings[i]);
      AStrings.Clear;
      Formats.NullValues.Clear;
      ReadSection(NULL_VALUES, AStrings);
      for i := 0 to AStrings.Count - 1 do
        Formats.NullValues.Add(AStrings[i]);
      AStrings.Clear;
      FieldFormats.Clear;
      for i := 0 to DestinationColCount - 1 do
      begin
        str := DATA_FORMATS + DestinationColName(i);
        ReadSection(str, AStrings);
        if AStrings.Count = 0 then
          Continue;
        with FieldFormats.Add do
        begin
          FieldName := DestinationColName(i);
          GeneratorValue := ReadInteger(str, DF_GENERATOR_VALUE, 0);
          GeneratorStep := ReadInteger(str, DF_GENERATOR_STEP, 0);
          ConstantValue := ReadString(str, DF_CONSTANT_VALUE, EmptyStr);
          NullValue := ReadString(str, DF_NULL_VALUE, EmptyStr);
          DefaultValue := ReadString(str, DF_DEFAULT_VALUE, EmptyStr);
          LeftQuote := ReadString(str, DF_LEFT_QUOTE, EmptyStr);
          RightQuote := ReadString(str, DF_RIGHT_QUOTE, EmptyStr);
          QuoteAction := TQuoteAction(ReadInteger(str, DF_QUOTE_ACTION, 0));
          CharCase := TQImportCharCase(ReadInteger(str, DF_CHAR_CASE, 0));
          CharSet := TQImportCharSet(ReadInteger(str, DF_CHAR_SET, 0));
        end;
      end;
      CommitAfterDone := ReadBool(QI_BASE, QI_COMMIT_AFTER_DONE,
        CommitAfterDone);
      CommitRecCount := ReadInteger(QI_BASE, QI_COMMIT_REC_COUNT,
        CommitRecCount);
      ImportRecCount := ReadInteger(QI_BASE, QI_IMPORT_REC_COUNT,
        ImportRecCount);
      ErrorLog := ReadBool(QI_BASE, QI_ENABLE_ERROR_LOG, ErrorLog);
      ErrorLogFileName := ReadString(QI_BASE, QI_ERROR_LOG_FILE_NAME,
        ErrorLogFileName);
      RewriteErrorLogFile := ReadBool(QI_BASE, QI_REWRITE_ERROR_LOG_FILE,
        RewriteErrorLogFile);
      ShowErrorLog := ReadBool(QI_BASE, QI_SHOW_ERROR_LOG, ShowErrorLog);
//      SQLLog := ReadBool(QI_BASE, QI_ENABLE_SQL_LOG, SQLLog);
//      SQLLogFileName := ReadString(QI_BASE, QI_SQL_LOG_FILE_NAME,
//        SQLLogFileName);
//      SQLLogFileRewrite := ReadBool(QI_BASE, QI_SQL_LOG_FILE_REWRITE,
//        SQLLogFileRewrite);

      ImportDestination := TQImportDestination(ReadInteger(QI_BASE,
        QI_IMPORT_DESTINATION, Integer(qidDataSet)));
      ImportMode := TQImportMode(ReadInteger(QI_BASE, QI_IMPORT_MODE,
        Integer(qimInsertAll)));
      AStrings.Clear;
      KeyColumns.Clear;
      ReadSection(QI_KEY_COLUMNS, AStrings);
      for i := 0 to AStrings.Count - 1 do
        KeyColumns.Add(AStrings[i]);
      {$IFNDEF NOGUI}
      GridCaptionRow := ReadInteger(QI_BASE, QI_GRID_CAPTION_ROW, -1);
      GridStartRow := ReadInteger(QI_BASE, QI_GRID_START_ROW, -1);
      {$ENDIF}
    finally
      AStrings.Free;
    end;
  end;
end;

procedure TQImport2.DoSaveConfiguration(IniFile: TIniFile);
var
  i: integer;
  str: string;
begin
  with IniFile do
  begin
    WriteString(QI_BASE, QI_FILE_NAME, Self.FileName);
    EraseSection(QI_MAP);
    for i := 0 to Map.Count - 1 do
      WriteString(QI_MAP, Map.Names[i], Map.Values[Map.Names[i]]);
    WriteString(BASE_FORMATS, BF_DECIMAL_SEPARATOR, Formats.DecimalSeparator);
    WriteString(BASE_FORMATS, BF_THOUSAND_SEPARATOR, Formats.ThousandSeparator);
    WriteString(BASE_FORMATS, BF_SHORT_DATE_FORMAT, Formats.ShortDateFormat);
    WriteString(BASE_FORMATS, BF_LONG_DATE_FORMAT, Formats.LongDateFormat);
    WriteString(BASE_FORMATS, BF_SHORT_TIME_FORMAT, Formats.ShortTimeFormat);
    WriteString(BASE_FORMATS, BF_LONG_TIME_FORMAT, Formats.LongTimeFormat);
    EraseSection(BOOLEAN_TRUE);
    for i := 0 to Formats.BooleanTrue.Count - 1 do
      WriteString(BOOLEAN_TRUE, Formats.BooleanTrue[i], EmptyStr);
    EraseSection(BOOLEAN_FALSE);
    for i := 0 to Formats.BooleanFalse.Count - 1 do
      WriteString(BOOLEAN_FALSE, Formats.BooleanFalse[i], EmptyStr);
    EraseSection(NULL_VALUES);
    for i := 0 to Formats.NullValues.Count - 1 do
      WriteString(NULL_VALUES, Formats.NullValues[i], EmptyStr);
    for i := 0 to FieldFormats.Count - 1 do begin
      str := FieldFormats[i].FieldName;
      if Trim(str) = EmptyStr then Continue;
      WriteString(str, DF_GENERATOR_VALUE, FieldFormats[i].FieldName);
      WriteInteger(str, DF_GENERATOR_VALUE, FieldFormats[i].GeneratorValue);
      WriteInteger(str, DF_GENERATOR_STEP, FieldFormats[i].GeneratorStep);
      WriteString(str, DF_CONSTANT_VALUE, FieldFormats[i].ConstantValue);
      WriteString(str, DF_NULL_VALUE, FieldFormats[i].NullValue);
      WriteString(str, DF_DEFAULT_VALUE, FieldFormats[i].DefaultValue);
      WriteString(str, DF_LEFT_QUOTE, FieldFormats[i].LeftQuote);
      WriteString(str, DF_RIGHT_QUOTE, FieldFormats[i].RightQuote);
      WriteInteger(str, DF_QUOTE_ACTION, Integer(FieldFormats[i].QuoteAction));
      WriteInteger(str, DF_CHAR_CASE, Integer(FieldFormats[i].CharCase));
      WriteInteger(str, DF_CHAR_SET, Integer(FieldFormats[i].CharSet));
    end;
    WriteBool(QI_BASE, QI_COMMIT_AFTER_DONE, CommitAfterDone);
    WriteInteger(QI_BASE, QI_COMMIT_REC_COUNT, CommitRecCount);
    WriteInteger(QI_BASE, QI_IMPORT_REC_COUNT, ImportRecCount);
    WriteBool(QI_BASE, QI_ENABLE_ERROR_LOG, ErrorLog);
    WriteString(QI_BASE, QI_ERROR_LOG_FILE_NAME, ErrorLogFileName);
    WriteBool(QI_BASE, QI_REWRITE_ERROR_LOG_FILE, RewriteErrorLogFile);
    WriteBool(QI_BASE, QI_SHOW_ERROR_LOG, ShowErrorLog);
//    WriteBool(QI_BASE, QI_ENABLE_SQL_LOG, SQLLog);
//    WriteString(QI_BASE, QI_SQL_LOG_FILE_NAME, SQLLogFileName);
//    WriteBool(QI_BASE, QI_SQL_LOG_FILE_REWRITE, SQLLogFileRewrite);

    WriteInteger(QI_BASE, QI_IMPORT_DESTINATION, Integer(ImportDestination));
    WriteInteger(QI_BASE, QI_IMPORT_MODE, Integer(ImportMode));
    EraseSection(QI_KEY_COLUMNS);
    for i := 0 to KeyColumns.Count - 1 do
      WriteString(QI_KEY_COLUMNS, KeyColumns[i], EmptyStr);
    {$IFNDEF NOGUI}
    WriteInteger(QI_BASE, QI_GRID_CAPTION_ROW, GridCaptionRow);
    WriteInteger(QI_BASE, QI_GRID_START_ROW, GridStartRow);
    {$ENDIF}
  end;
end;

procedure TQImport2.DestinationInsert;
{$IFNDEF NOGUI}
var
  i: integer;
{$ENDIF}
begin
  if IsCSV then Exit;
  case ImportDestination of
    qidDataSet:
      case AddType of
        qatAppend: DataSet.Append;
        qatInsert: DataSet.Insert;
      end;
    {$IFNDEF NOGUI}
    qidDbGrid:
      case AddType of
        qatAppend: DBGrid.DataSource.DataSet.Append;
        qatInsert: DBGrid.DataSource.DataSet.Insert;
      end;
    qidListView: begin
      FCurrListItem := ListView.Items.Add;
      for i := 1 to FImportRow.Count - 1 do
        FCurrListItem.SubItems.Add(EmptyStr);
    end;
    qidStringGrid: begin
      if FCurrStrGrRow = StringGrid.RowCount then
        StringGrid.RowCount := StringGrid.RowCount + 1;
      if FCurrStrGrRow = -1
        then FCurrStrGrRow := StringGrid.RowCount - 1;
    end;
    {$ENDIF}
  end;
  FLastAction := qiaInsert;
end;

procedure TQImport2.DestinationEdit;
begin
  if IsCSV then Exit;
  case ImportDestination of
    qidDataSet: DataSet.Edit;
    {$IFNDEF NOGUI}
    qidDbGrid: DBGrid.DataSource.DataSet.Edit;
    {$ENDIF}
  end;
  FLastAction := qiaUpdate;
end;

procedure TQImport2.DestinationDelete;
{$IFNDEF NOGUI}
var
  i: integer;
{$ENDIF}
begin
  if IsCSV then Exit;
  case ImportDestination of
    qidDataSet: DataSet.Delete;
    {$IFNDEF NOGUI}
    qidDbGrid: DBGrid.DataSource.DataSet.Delete;
    qidListView:
      if Assigned(FCurrListItem) then FCurrListItem.Delete;
    qidStringGrid:
      if (FCurrStrGrRow > -1) and (FCurrStrGrRow < StringGrid.RowCount) then begin
        for i := FCurrStrGrRow to StringGrid.RowCount - 2 do
          StringGrid.Rows[i].Assign(StringGrid.Rows[i+1]);
        StringGrid.RowCount:= StringGrid.RowCount - 1;
      end;
    {$ENDIF}
  end;
  FLastAction := qiaDelete;
end;

procedure TQImport2.DestinationSetValues;
var
  i: integer;
  str: string;
  errorMsg: string;
begin
  for i := 0 to FImportRow.Count - 1 do
  begin
    if FIsCSV then
    begin
      str := FImportRow[i].Value;
      if FQuote <> #0 then
        str := AnsiQuotedStr(str, FQuote);
      if i = FImportRow.Count - 1
        then str := str + {$IFDEF WIN32}#13#10{$ENDIF}{$IFDEF LINUX}#10{$ENDIF}
        else str := str + FComma;
      FStream.Write(str[1], Length(str));
      Continue;
    end;
    case ImportDestination of
      qidDataSet:
        begin
          errorMsg := StringToField(DataSet.Fields[FImportRow[i].FColumnIndex],
            FImportRow[i].Value, FImportRow[i].IsBinary);
          if errorMsg <> EmptyStr then
            WriteErrorLog(errorMsg);
        end;
      {$IFNDEF NOGUI}
      qidDBGrid:
        begin
          errorMsg := StringToField(DBGrid.Columns[FImportRow[i].FColumnIndex].Field,
            FImportRow[i].Value, FImportRow[i].IsBinary);
          if errorMsg <> EmptyStr then
            WriteErrorLog(errorMsg);
        end;
      qidListView:
        if Assigned(FCurrListItem) then
        begin
          if FImportRow[i].FColumnIndex = 0
            then FCurrListItem.Caption :=  FImportRow[i].Value
            else FCurrListItem.SubItems[FImportRow[i].FColumnIndex - 1] :=
              FImportRow[i].Value;
        end;
      qidStringGrid:
        if FCurrStrGrRow > -1 then
          StringGrid.Cells[FImportRow[i].FColumnIndex, FCurrStrGrRow] :=
            FImportRow[i].Value;
      {$ENDIF}
    end;
  end;
end;

procedure TQImport2.DestinationPost;
begin
  if IsCSV then Exit;
  case ImportDestination of
    qidDataSet:
      if DataSet.State in [dsInsert, dsEdit] then
        DataSet.Post;
{$IFNDEF NOGUI}
    qidDbGrid:
      if DBGrid.DataSource.DataSet.State in [dsInsert, dsEdit] then
        DBGrid.DataSource.DataSet.Post;
    qidStringGrid:
      Inc(FCurrStrGrRow);
{$ENDIF}
  end;
end;

procedure TQImport2.DestinationCancel;
begin
  if IsCSV then Exit;
  case ImportDestination of
    qidDataSet: if DataSet.State in [dsInsert, dsEdit] then DataSet.Cancel;
    {$IFNDEF NOGUI}
    qidDbGrid: if DBGrid.DataSource.DataSet.State in [dsInsert, dsEdit] then
      DBGrid.DataSource.DataSet.Cancel;
    {$ENDIF}
  end;
end;

function TQImport2.DestinationFindColumn(const ColName: string): integer;
var
  Field: TField;
{$IFNDEF NOGUI}
  i: integer;
{$ENDIF}
begin
  Result := -1;
  if FIsCSV then Exit;
  case ImportDestination of
    qidDataSet: begin
      Field := DataSet.FindField(ColName);
      if Assigned(Field) then
        Result := Field.Index;
    end;
    {$IFNDEF NOGUI}
    qidDBGrid:
      for i := 0 to DBGrid.Columns.Count - 1 do
        if AnsiCompareText(DBGrid.Columns[i].Title.Caption, ColName) = 0 then
        begin
          Result := i;
          Exit;
        end;
    qidListView:
      for i := 0 to ListView.Columns.Count - 1 do
        if AnsiCompareText(ListView.Columns[i].Caption, ColName) = 0 then
        begin
          Result := i;
          Exit;
        end;
    qidStringGrid: begin
      i := StrToIntDef(ColName, -1);
      if i > -1 then
      begin
        if  i < StringGrid.ColCount then Result := i;
      end
      else begin
        if GridCaptionRow > -1 then
          for i := 0 to StringGrid.ColCount - 1 do
            if AnsiCompareStr(StringGrid.Cells[i, GridCaptionRow], ColName) = 0 then
            begin
              Result := i;
              Exit;
            end;
      end;
    end;
    {$ENDIF}
  end;
end;

procedure TQImport2.DestinationDisableControls;
begin
  if FIsCSV then Exit;
  case ImportDestination of
    qidDataSet: DataSet.DisableControls;
    {$IFNDEF NOGUI}
    qidDBGrid: DBGrid.DataSource.DataSet.DisableControls;
    qidListView: ListView.Items.BeginUpdate;
    {$ENDIF}
  end;
end;

procedure TQImport2.DestinationEnableControls;
begin
  if FIsCSV then Exit;
  case ImportDestination of
    qidDataSet: DataSet.EnableControls;
    {$IFNDEF NOGUI}
    qidDBGrid: DBGrid.DataSource.DataSet.EnableControls;
    qidListView: ListView.Items.EndUpdate;
    {$ENDIF}
  end;
end;

procedure TQImport2.CheckDestination;
begin
  QImportCheckDestination(IsCSV, ImportDestination, DataSet
    {$IFNDEF NOGUI}, DBGrid, ListView,
 StringGrid{$ENDIF});
end;

function TQImport2.DestinationFindByKey: boolean;
var
  Keys: string;
  Values: Variant;
  i{$IFNDEF NOGUI}, j{$ENDIF}: integer;
  Col: TQImportCol;
  {$IFNDEF NOGUI}Flag: boolean;
{$ENDIF}
begin
  Result := false;
  if FIsCSV then Exit;
  case ImportDestination of
    qidDataSet:
      if KeyColumns.Count > 0 then
      begin
        Keys := EmptyStr;
        if KeyColumns.Count > 1 then
          Values := VarArrayCreate([0, KeyColumns.Count - 1], varVariant);
        for i := 0 to KeyColumns.Count - 1 do
        begin
          Col := FImportRow.ColByName(KeyColumns[i]);
          if Assigned(Col) then
          begin
            Keys := Keys + Col.Name;
            if KeyColumns.Count > 1
              then Values[i] := Col.Value
              else Values := Col.Value;
          end;
          if i < KeyColumns.Count - 1 then
            Keys := Keys + ';';
        end;
        if Assigned(FOnDestinationLocate) then
          FOnDestinationLocate(Self, KeyColumns, FImportRow, Keys, Values);
        try
          Result := DataSet.Locate(Keys, Values, [loCaseInsensitive])
        except
          Result := false;
        end;
      end;
    {$IFNDEF NOGUI}
    qidDBGrid:
      if KeyColumns.Count > 0 then
      begin
        Keys := EmptyStr;
        if KeyColumns.Count > 1 then
          Values := VarArrayCreate([0, KeyColumns.Count - 1], varVariant);
        for i := 0 to KeyColumns.Count - 1 do
        begin
          Col := FImportRow.ColByName(KeyColumns[i]);
          if Assigned(Col) then
          begin
            Keys := Keys + DBGrid.Columns[Col.FColumnIndex].Field.FieldName;
            if KeyColumns.Count > 1
              then Values[i] := Col.Value
              else Values := Col.Value;
          end;
          if i < KeyColumns.Count - 1 then Keys := Keys + ';';
        end;
        if Assigned(FOnDestinationLocate) then
          FOnDestinationLocate(Self, KeyColumns, FImportRow, Keys, Values);
        Result := DBGrid.DataSource.DataSet.Locate(Keys, Values, [loCaseInsensitive])
      end;
    qidListView: begin
      for i := 0 to ListView.Items.Count - 1 do
      begin
        Flag := true;
        for j := 0 to KeyColumns.Count - 1 do
        begin
          Col := FImportRow.ColByName(KeyColumns[j]);
          if Assigned(Col) then
          begin
            if Col.FColumnIndex = 0
              then Flag := AnsiCompareText(Col.Value, ListView.Items[i].Caption) = 0
              else Flag := AnsiCompareText(Col.Value, ListView.Items[i].SubItems[Col.FColumnIndex - 1]) = 0;
            if not Flag then Break;
          end
          else Exit;
        end;
        if Flag then
        begin
          Result := true;
          FCurrListItem := ListView.Items[i];
          Exit;
        end;
      end;
    end;
    qidStringGrid: begin
      for i := 0 to StringGrid.RowCount - 1 do
      begin
        if (GridCaptionRow > -1) and (i = GridCaptionRow) then
          Continue;
        Flag := true;
        for j := 0 to KeyColumns.Count - 1 do
        begin
          Col := FImportRow.ColByName(KeyColumns[j]);
          if Assigned(Col)
            then Flag := AnsiCompareText(Col.Value, StringGrid.Cells[Col.FColumnIndex, i]) = 0
            else Exit;
          if not Flag then
            Break;
        end;
        if Flag then
        begin
          Result := true;
          FCurrStrGrRow := i;
          Exit;
        end;
      end;
    end;
    {$ENDIF}
  end;
end;

function TQImport2.DestinationColCount: integer;
begin
  Result := QImportDestinationColCount(IsCSV, ImportDestination, DataSet
    {$IFNDEF NOGUI}, DBGrid, ListView, StringGrid{$ENDIF});
end;

function TQImport2.DestinationColName(Index: integer): string;
begin
  Result := QImportDestinationColName(IsCSV, ImportDestination, DataSet,
    {$IFNDEF NOGUI}DBGrid, ListView, StringGrid, GridCaptionRow,{$ENDIF} Index);
end;

procedure TQImport2.DoBeginImport;
begin
  if Assigned(FOnBeforeImport) then FOnBeforeImport(Self);
end;

function TQImport2.DoBeforePost: boolean;
begin
  Result := true;
  if Assigned(FOnBeforePost) then FOnBeforePost(Self, FImportRow, Result);
end;

procedure TQImport2.DoAfterPost;
(*const
  LF = {$IFDEF WIN32}#13#10{$ENDIF}{$IFDEF LINUX}#10{$ENDIF};
  sINSERT = 'INSERT INTO %s (%s)' + LF + '  VALUES (%s);';
  sUPDATE = 'UPDATE %s' + LF + 'SET    %s ' + LF + 'WHERE  %s;';
  sDELETE = 'DELETE' + LF + 'FROM   %s' + LF + 'WHERE  %s;';*)
{var
  i, j, k: integer;
  str, str1, str2: string;
  FField: TField;
  IsNull: boolean;}
begin
{  if FSQLLog and Assigned(FSQL) then begin
    k := 0;
    str := EmptyStr;
    str1 := EmptyStr;
    str2 := EmptyStr;
    for i := 0 to FImportRow.Count - 1 do begin

      IsNull := false;
      for j := 0 to Formats.NullValues.Count - 1 do begin
        IsNull :=  AnsiCompareStr(FImportRow[i].Value, Formats.NullValues[j]) = 0;
        if IsNull then Break;
      end;

      if IsNull then str2 := 'NULL'
      else begin
        str2 := FImportRow[i].Value;
        FField := FDataSet.FindField(Row[i].Name);
        if Assigned(FField) then
          case FField.DataType of
            ftString,
            ftWideString,
            ftFixedChar: str2 := AnsiQuotedStr(str2, '''');

            ftFloat,
            ftCurrency,
            ftBCD: begin
              for j := Length(str2) downto 1 do
                if str2[j] = SysUtils.ThousandSeparator then
                  Delete(str2, j, 1)
                else if str2[j] = SysUtils.DecimalSeparator then begin
                  Delete(str2, j, 1);
                  Insert('.', str2, j);
                end;
            end;

            ftDate,
            ftTime,
            ftDateTime: str2 := FormatDateTime('yyyy-mm-dd', StrToDate(str2));
          end;
      end;

      j := 0;
      case Row.QImport.LastAction of
        qiaInsert: begin
          str := str + FormatIdentifier(Row[i].Name, CurrentDatabase);
          str1 := str1 + str2;
          if i < Row.Count - 1 then begin
            str := str + ', ';
            str1 := str1 + ', ';
          end;
        end;
        qiaUpdate: begin
          if Row.QImport.KeyColumns.IndexOf(Row[i].Name) > - 1 then begin
            str1 := str1 + FormatIdentifier(Row[i].Name, CurrentDatabase) + ' = ' + str2;
            Inc(j);
            if j < Row.QImport.KeyColumns.Count then
              str1 := str1 + LF + 'AND    ';
          end
          else begin
            if str <> EmptyStr then str := str + '       ';
            str := str + FormatIdentifier(Row[i].Name, CurrentDatabase) + ' = ' + str2 ;
            Inc(k);
            if k < Row.Count - Row.QImport.KeyColumns.Count then
              str := str + ',' + LF;
          end;
        end;
        qiaDelete: begin
          if Row.QImport.KeyColumns.IndexOf(Row[i].Name) > - 1 then begin
            str := str + FormatIdentifier(Row[i].Name, CurrentDatabase) + ' = ' + str2;
            Inc(j);
            if j < Row.QImport.KeyColumns.Count then
              str := str + LF + 'AND    ';
          end
        end;
      end;
    end;
    case Row.QImport.LastAction of
      qiaInsert: str := Format(sINSERT,
        [FormatIdentifier(TableName, CurrentDatabase), str, str1]) + LF + LF;
      qiaUpdate: str := Format(sUPDATE,
        [FormatIdentifier(TableName, CurrentDatabase), str, str1]) + LF + LF;
      qiaDelete: str := Format(sDELETE,
        [FormatIdentifier(TableName, CurrentDatabase), str]) + LF + LF;
    end;
      FSQLLog.Write(str[1], Length(str));
  end;}

  if Assigned(FOnAfterPost) then FOnAfterPost(Self, FImportRow);
end;

procedure TQImport2.DoImportRecord;
begin
  Inc(FImportedRecs);
  if Assigned(FOnImportRecord) then
    FOnImportRecord(Self);
end;

procedure TQImport2.DoImportError(Error: Exception);
var
  lastError: string;
begin
  if FErrorLog then
  begin
    lastError := Format({$IFDEF WIN32}QImportLoadStr(QIW_ImportErrorFormat){$ENDIF}
                  {$IFDEF LINUX}QIW_ImportErrorFormat{$ENDIF},
                  [FormatDateTime(Formats.ShortDateFormat + ' ' +
                   Formats.ShortTimeFormat, Now), FCurrentLineNumber, Error.Message]);
    FErrors.Add(lastError);
    if Assigned(FErrorLogFS) then
    begin
      lastError := lastError + LF;
      FErrorLogFS.Write(lastError[1], Length(lastError));
    end;
  end;

  Inc(FCurrentLineNumber);

  if Assigned(FOnImportError) then
    FOnImportError(Self);
end;

procedure TQImport2.WriteErrorLog(const ErrorMsg: string);
var
  lastError: string;
begin
  if FErrorLog then
  begin
    lastError := Format({$IFDEF WIN32}QImportLoadStr(QIW_ImportErrorFormat){$ENDIF}
                  {$IFDEF LINUX}QIW_ImportErrorFormat{$ENDIF},
                  [FormatDateTime(Formats.ShortDateFormat + ' ' +
                   Formats.ShortTimeFormat, Now), FCurrentLineNumber, ErrorMsg]);
    
    if Assigned(FErrorLogFS) then
    begin
      lastError := lastError + LF;
      FErrorLogFS.Write(lastError[1], Length(lastError));
    end;
  end;

  if Assigned(FOnImportErrorAdv) then
    FOnImportErrorAdv(Self);
end;

procedure TQImport2.DoNeedCommit;
begin
  if Assigned(FOnNeedCommit) then
    FOnNeedCommit(Self);
end;

procedure TQImport2.DoEndImport;
begin
  if Assigned(FOnAfterImport) then FOnAfterImport(Self);
end;

procedure TQImport2.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited;
  if (AComponent = FDataSet) and (Operation = opRemove)
    then FDataSet := nil;
  {$IFNDEF NOGUI}
  if (AComponent = FDBGrid) and (Operation = opRemove)
    then FDBGrid := nil;
  if (AComponent = FListView) and (Operation = opRemove)
    then FListView := nil;
  if (AComponent = FStringGrid) and (Operation = opRemove)
    then FStringGrid := nil;
  {$ENDIF}
end;

procedure TQImport2.InitializeImportRow;

  function IsKeyColumn(const AColumnName: string): Boolean;
  var
    i: Integer;
  begin
    Result := False;
    for i := 0 to KeyColumns.Count - 1 do
    begin
      if CompareStr(KeyColumns[i], AColumnName) = 0 then
      begin
        Result := True;
        Break;
      end;
    end;
  end;

var
  i, k: integer;
  Col: TQImportCol;
begin
  FImportRow.Clear;
  FImportRow.FIndex.Clear;
  FMappedColumns.Clear;

  for i := 0 to FMap.Count - 1 do begin
    k := DestinationFindColumn(FMap.Names[i]);
    if (k > -1) or IsCSV or (FImportDestination = qidUserDefined) then begin
      with FImportRow.Add(FMap.Names[i]) do FColumnIndex := k;
      FImportRow.FIndex.AddObject(FMap.Names[i], TObject(i));
      if not IsKeyColumn(FMap.Names[i]) then
        FMappedColumns.Add(FMap.Names[i]);
    end;
    {if Assigned(FDataSet.FindField(FMap.Names[i])) then begin
      FImportRow.Add(FMap.Names[i]);
      FImportRow.FIndex.AddObject(FMap.Names[i], TObject(i));
    end;}
  end;
  FImportRow.FIndex.Sort;

  for i := 0 to FFieldFormats.Count - 1 do
  begin
    Col := FImportRow.ColByName(FFieldFormats.Items[i].FieldName);
    if not Assigned(Col) {ab}and ((FFieldFormats[i].GeneratorStep <> 0) or
      (FFieldFormats[i].ConstantValue <> '')){/ab} then
    begin
      k := DestinationFindColumn(FFieldFormats.Items[i].FieldName);
      if k > -1 then
      begin
        Col := FImportRow.Add(FFieldFormats.Items[i].FieldName);
        Col.FColumnIndex := k;
      end;
    end;
    {if not Assigned(Col) and
       Assigned(FDataSet.FindField(FFieldFormats.Items[i].FieldName)) then
      FImportRow.Add(FFieldFormats.Items[i].FieldName);}

    // ab Col := FImportRow.ColByName(FFieldFormats.Items[i].FieldName);
    if Assigned(Col) and (FFieldFormats[i].GeneratorStep <> 0) then
      FImportGenerators.Add(FFieldFormats.Items[i].FieldName,
        FFieldFormats[i].GeneratorValue, FFieldFormats[i].GeneratorStep);
  end;
end;

procedure TQImport2.BeforeImport;
var
  ErrorLogMode: word;
  str: string;
  ELFN: string;
{var
  SQLLogMode: word;}
begin
  if (FImportRecCount > 0)  and (ImportRecCount < FTotalRecCount)
    then FTotalRecCount := FImportRecCount;

  FLastAction := qiaNone;
  FImportedRecs := 0;
  FErrors.Clear;
  FCanceled := false;
  FFormats.StoreFormats;
  FFormats.ApplyParams;
  DestinationDisableControls;
  FCurrentLineNumber := 1;

  {$IFNDEF NOGUI}
  FCurrListItem := nil;
  FCurrStrGrRow := FGridStartRow;
  {$ENDIF}

  InitializeImportRow;

  if FErrorLog and (FErrorLogFileName <> EmptyStr) then
  begin
    if FRewriteErrorLogFile
      then ErrorLogMode := fmCreate
      else ErrorLogMode := fmOpenReadWrite;

    ELFN := FErrorLogFileName;
    str := ExtractFilePath(ELFN);
    if str = EmptyStr then
      ELFN := ExtractFilePath(ParamStr(0)) + ELFN
    else if not DirectoryExists(str) then
      ForceDirectories(str);

    FErrorLogFS := TFileStream.Create(ELFN, ErrorLogMode);
    FErrorLogFS.Position := FErrorLogFS.Size;

    str := Format({$IFDEF WIN32}QImportLoadStr(QIW_ErrorLogStarted){$ENDIF}
                  {$IFDEF LINUX}QIW_ErrorLogStarted{$ENDIF},
      [FormatDateTime(Formats.ShortDateFormat + ' ' + Formats.ShortTimeFormat,
        Now)]) + LF + LF;
    FErrorLogFS.Write(str[1], Length(str));
  end;

{  if FSQLLog and (SQLLogFileName <> EmptyStr) then begin
    if FSQLLogFileRewrite
      then SQLLogMode := fmCreate
      else SQLLogMode := fmOpenReadWrite;
    FSQL := TFileStream.Create(SQLLogFileName, SQLLogMode);
    FSQL.Position := FSQL.Size;
  end;}
end;

procedure TQImport2.DoImport;
var
  ImpRes: TQImportResult;
begin
  StartImport;
  try
    Inc(FCurrentLineNumber, FSkipFirstRows);
    while CheckCondition do
    begin
      FLastAction := qiaNone;
      if not Skip then
      begin
        FillImportRow;
        if FImportDestination = qidUserDefined then
        begin
          if Assigned(FOnUserDefinedImport) then
            FOnUserDefinedImport(Self, FImportRow);
        end
        else begin
          if Assigned(FOnImportRowComplete) then
            FOnImportRowComplete(Self, FImportRow);
          ImpRes := ImportData;
          case ImpRes of
            qirBreak: Break;
            qirContinue:
              begin
                ChangeCondition;
                Continue;
              end
          end;
        end;
      end
      else
        Inc(FCurrentLineNumber);
      ChangeCondition;
{$IFDEF WIN32}
      Sleep(0);
{$ENDIF}
    end;
  finally
    FinishImport;
  end;
end;

procedure TQImport2.DataManipulation;
begin
  if IsCSV then
  begin
    DestinationSetValues;
  end
  else begin
    case ImportMode of
      qimInsertAll:
        if DoBeforePost then
          DestinationInsert;
      qimInsertNew:
        if not DestinationFindByKey and
           not DestinationFindByFields and DoBeforePost then
          DestinationInsert;
      qimUpdate:
        if DestinationFindByKey and DoBeforePost then
          DestinationEdit;
      qimUpdateOrInsert:
        if DestinationFindByKey then
        begin
          if DoBeforePost then
            DestinationEdit
        end
        else begin
          if not DestinationFindByFields and DoBeforePost then
            DestinationInsert;
        end;
      qimDelete:
        if DestinationFindByKey and DoBeforePost then
          DestinationDelete;
      qimDeleteOrInsert:
        if DestinationFindByKey then
        begin
          if DoBeforePost then
            DestinationDelete
        end
        else begin
          if not DestinationFindByFields and DoBeforePost then
            DestinationInsert;
        end;
    end;

    if LastAction in [qiaInsert, qiaUpdate] then
    begin
      DestinationSetValues;
      DestinationPost;
    end;
    DoAfterPost;
  end;
  DoImportRecord;
  Inc(FCurrentLineNumber);
end;

procedure TQImport2.AfterImport;
var
  i: integer;
  str: string;
//  Path, FLogName: string;
begin
  FFormats.RestoreFormats;
  for i := FImportRow.Count - 1 downto 0 do FImportRow.Delete(i);
//  if FImportGenerators.Count > 0 then
    for i := FImportGenerators.Count - 1 downto 0 do FImportGenerators.Delete(i);
  DestinationEnableControls;

  // Saving ErrorLog To File
{  if (FErrors.Count > 0) and (FErrorLogFileName <> EmptyStr) then begin
    FLogName := FErrorLogFileName;
    Path := ExtractFilePath(FLogName);
    if Path = EmptyStr then begin
      GetDir(0, Path);
      Path := IncludeTrailingBackSlash(Path);
      FLogName := Path + FLogName;
    end;
    if not DirectoryExists(Path) then
      ForceDirectories(Path);
    if DirectoryExists(Path) then
      FErrors.SaveToFile(FLogName);
  end;}

  if Assigned(FErrorLogFS) then begin
    str := Format({$IFDEF WIN32}QImportLoadStr(QIW_ErrorLogFinished){$ENDIF}
                  {$IFDEF LINUX}QIW_ErrorLogFinished{$ENDIF},
      [FormatDateTime(Formats.ShortDateFormat + ' ' + Formats.ShortTimeFormat,
        Now)]) + LF;
    if FErrors.Count > 0 then
      str := LF + str;
    FErrorLogFS.Write(str[1], Length(str));

    if FErrors.Count > 0
      then str := Format({$IFDEF WIN32}QImportLoadStr(QIW_SomeErrorsFound){$ENDIF}
                         {$IFDEF LINUX}QIW_SomeErrorsFound{$ENDIF},
                         [FErrors.Count]) + LF + LF
      else str := {$IFDEF WIN32}QImportLoadStr(QIW_NoErrorsFound){$ENDIF}
                  {$IFDEF LINUX}QIW_NoErrorsFound{$ENDIF} + LF + LF;
    FErrorLogFS.Write(str[1], Length(str));

    FErrorLogFS.Free;
    FErrorLogFS := nil;

{$IFDEF WIN32}
    if ShowErrorLog {and (FErrors.Count > 0)} then
      ShellExecute(0, 'open', PChar(FErrorLogFileName), '', '', SW_SHOWNORMAL);
{$ENDIF}
  end;

{  if Assigned(FSQL) then begin
    FSQL.Free;
    FSQL := nil;
  end;}
end;

procedure TQImport2.DoAfterSetFileName;
begin
//
end;

function TQImport2.CheckProperties: boolean;
var
  i: integer;
begin
  Result := false;
  CheckDestination;
  if FileName = EmptyStr then
    raise EQImportError.Create({$IFDEF WIN32}QImportLoadStr(QIE_NoFileName){$ENDIF}
                               {$IFDEF LINUX}QIE_NoFileName{$ENDIF});
  if not FileExists(FileName) then
    raise EQImportError.CreateFmt({$IFDEF WIN32}QImportLoadStr(QIE_FileNotExists){$ENDIF}
                                  {$IFDEF LINUX}QIE_FileNotExists{$ENDIF}, [FFileName]);
  if FMap.Count = 0 then
    raise EQImportError.Create({$IFDEF WIN32}QImportLoadStr(QIE_MappingEmpty){$ENDIF}
                               {$IFDEF LINUX}QIE_MappingEmpty{$ENDIF});

  if not (IsCSV or (FImportDestination = qidUserDefined)) then
    for i := 0 to FMap.Count - 1 do
      if DestinationFindColumn(FMap.Names[i]) = -1 then
        raise EQImportError.CreateFmt({$IFDEF WIN32}QImportLoadStr(QIE_FieldNotFound){$ENDIF}
                                      {$IFDEF LINUX}QIE_FieldNotFound{$ENDIF}, [FMap.Names[i]]);

  if ImportMode <> qimInsertAll then begin
    if KeyColumns.Count = 0 then
      raise EQImportError.Create({$IFDEF WIN32}QImportLoadStr(QIE_KeyColumnsNotDefined){$ENDIF}
                                 {$IFDEF LINUX}QIE_KeyColumnsNotDefined{$ENDIF});
    for i := 0 to KeyColumns.Count - 1 do
      if DestinationFindColumn(KeyColumns[i]) = -1 then
        raise EQImportError.CreateFmt({$IFDEF WIN32}QImportLoadStr(QIE_KeyColumnNotFound){$ENDIF}
                                      {$IFDEF LINUX}QIE_KeyColumnNotFound{$ENDIF}, [KeyColumns[i]]);
  end;

  if not Result then Result := true;
end;

procedure TQImport2.DoUserDataFormat(Col: TQImportCol);
var
  i, j: integer;
  {$IFDEF WIN32}p: PChar;{$ENDIF}
  Gen: TQImportGenerator;
  AllowReplace: boolean;
  textToFind, replaceWith, colValue: string;
begin
  if not Assigned(Col) then Exit;
  i := FFieldFormats.IndexByName(Col.Name);
  if i = -1 then Exit;

  AllowReplace := true;

  // generator
  Gen := FImportGenerators.GenByName(Col.Name);
  if Assigned(Gen) then
  begin
    Col.Value := IntToStr(Gen.GetNewValue);
    Exit;
  end;
  // default value
  if (FFieldFormats[i].DefaultValue <> EmptyStr) and
     (FFieldFormats[i].NullValue <> EmptyStr) and
     (((FFieldFormats[i].NullValue = '''''') and (Col.Value = EmptyStr)) or
      (AnsiCompareText(Col.Value, FFieldFormats[i].NullValue) = 0)) then
  begin
    if FFieldFormats[i].DefaultValue = '''''' then
      FFieldFormats[i].DefaultValue := EmptyStr;
    Col.Value := FFieldFormats[i].DefaultValue;
    AllowReplace := false;
  end;
  // constant value
  if (FFieldFormats[i].ConstantValue <> EmptyStr) then
  begin
    if FFieldFormats[i].ConstantValue = '''''' then
      Col.Value := EmptyStr
    else
      Col.Value := FFieldFormats[i].ConstantValue;
    AllowReplace := false;
  end;
  // replacements
  if AllowReplace then
  begin
    for j := 0 to FFieldFormats[i].Replacements.Count - 1 do
    begin
      textToFind := FFieldFormats[i].Replacements[j].TextToFind;
      replaceWith := FFieldFormats[i].Replacements[j].ReplaceWith;
      colValue := Col.Value;
      if FFieldFormats[i].Replacements[j].IgnoreCase then
        StrReplace(colValue, textToFind, replaceWith, [rfReplaceAll, rfIgnoreCase])
      else
        StrReplace(colValue, textToFind, replaceWith, [rfReplaceAll]);
      Col.Value := colValue;  
    end;
  end;
  // quote action
  case FFieldFormats[i].QuoteAction of
    qaAdd: Col.Value := AddQuote(Col.Value, FFieldFormats[i].LeftQuote,
      FFieldFormats[i].RightQuote);
    qaRemove: Col.Value := RemoveQuote(Col.Value, FFieldFormats[i].LeftQuote,
      FFieldFormats[i].RightQuote);
  end;
  // char case
  case FFieldFormats[i].CharCase of
    iccUpper: Col.Value := AnsiUpperCase(Col.Value);
    iccLower: Col.Value := AnsiLowerCase(Col.Value);
    iccUpperFirst: Col.Value := AnsiUpperFirst(Col.Value);
    iccUpperFirstWord: Col.Value := AnsiUpperFirstWord(Col.Value);
  end;
  // char set
  {$IFDEF WIN32}
  if Col.Value <> EmptyStr then
  begin
    p := PChar(Col.Value);
    case FFieldFormats[i].CharSet of
      icsAnsi: begin
        OemToChar(PChar(Col.Value), p);
        Col.Value := string(p);
      end;
      icsOem: begin
        CharToOem(PChar(Col.Value), p);
        Col.Value := string(p);
      end;
    end;
  end;
  {$ENDIF}
end;

function TQImport2.CanContinue: boolean;
begin
  Result := not FCanceled;
  if Assigned(FOnImportCancel) then FOnImportCancel(Self, Result);
  FCanceled := not Result;
end;

procedure TQImport2.SetDataSet(const Value: TDataSet);
begin
  if FDataSet <> Value then FDataSet := Value;
end;

{$IFNDEF NOGUI}
procedure TQImport2.SetDBGrid(const Value: TDBGrid);
begin
  if FDBGrid <> Value then FDBGrid := Value;
end;

procedure TQImport2.SetListView(const Value: TListView);
begin
  if FListView <> Value then FListView := Value;
end;

procedure TQImport2.SetStringGrid(const Value: TStringGrid);
begin
  if FStringGrid <> Value then FStringGrid := Value;
end;
{$ENDIF}

procedure TQImport2.SetKeyColumns(const Value: TStrings);
begin
  FKeyColumns.Assign(Value);
end;

procedure TQImport2.SetFileName(const Value: string);
begin
  if FFileName <> Value then begin
    FFileName := Value;
    DoAfterSetFileName;
  end;
end;

procedure TQImport2.SetMap(const Value: TStrings);
begin
  FMap.Assign(Value);
end;

function TQImport2.GetErrorRecs: integer;
begin
  Result := FErrors.Count;
end;

procedure TQImport2.SetFormats(const Value: TQImportFormats);
begin
  FFormats.Assign(Value);
end;

procedure TQImport2.SetFieldFormats(const Value: TQImportFieldFormats);
begin
  FFieldFormats.Assign(Value);
end;

function TQImport2.StringToField(Field: TField; const Str: string;
  IsBinary: boolean): string;
var
  sTmp: string;

  function StrIsNull(const Str: string): boolean;
  var
    i: integer;
  begin
    Result := false;
    for i := 0 to FFormats.NullValues.Count - 1 do
    begin
      Result := AnsiCompareText(Str, FFormats.NullValues[i]) = 0;
      if Result then Exit;
    end;
  end;

  function StringToBoolean(const Str: string): boolean;
  begin
    Result := FFormats.FBooleanTrue.IndexOf(Str) > -1;
  end;

  {function GetRawString(const Str: string): string;
  var
    l, r: integer;
  begin
    Result := Trim(Str);
    if ((FFormats.LeftQuotation = '') and (FFormats.RightQuotation = '')) or
       (FFormats.QuotationAction = qaNone) then Exit;

    case FFormats.QuotationAction of
      qaAdd: Result := FFormats.LeftQuotation + Result + FFormats.RightQuotation;
      qaRemove: begin
        l := Length(FFormats.LeftQuotation);
        r := Length(FFormats.RightQuotation);
        while (Copy(Result, 1, l) = FFormats.LeftQuotation) and
              (Copy(Result, Length(Result) - r + 1, r) = FFormats.RightQuotation) do begin
          Delete(Result, 1, l);
          Delete(Result, Length(Result) - r + 1, r);
        end;
      end;
    end;
  end;}

//{$IFNDEF VCL5}
//  type
//    TReplaceFlags = set of (rfReplaceAll, rfIgnoreCase);
//
//  function StringReplace(const S, OldPattern, NewPattern: string;
//    Flags: TReplaceFlags): string;
//  var
//    SearchStr, Patt, NewStr: string;
//    Offset: Integer;
//  begin
//    if rfIgnoreCase in Flags then
//    begin
//      SearchStr := AnsiUpperCase(S);
//      Patt := AnsiUpperCase(OldPattern);
//    end else
//    begin
//      SearchStr := S;
//      Patt := OldPattern;
//    end;
//    NewStr := S;
//    Result := '';
//    while SearchStr <> '' do
//    begin
//      Offset := AnsiPos(Patt, SearchStr);
//      if Offset = 0 then
//      begin
//        Result := Result + NewStr;
//        Break;
//      end;
//      Result := Result + Copy(NewStr, 1, Offset - 1) + NewPattern;
//      NewStr := Copy(NewStr, Offset + Length(OldPattern), MaxInt);
//      if not (rfReplaceAll in Flags) then
//      begin
//        Result := Result + NewStr;
//        Break;
//      end;
//      SearchStr := Copy(SearchStr, Offset + Length(Patt), MaxInt);
//    end;
//  end;
//{$ENDIF}

var
  DT: TDateTime;
  tmpStr: string;
begin
  Result := EmptyStr;
  try
    if {(Length(Str) > 0) and} Assigned(Field) then
    begin
      if StrIsNull(Trim(Str)) then
      begin
        Field.AsVariant := NULL;
        Exit;
      end;
      case GetImportFieldType(Field) of
        iftString:
          if IsBinary
            then Field.AsString := Str
            else Field.AsString := {GetRawString(}Str{)};
        iftInteger:
          begin
            try
              if Str <> '' then
              begin
                tmpStr := StringReplace(GetNumericString(Str), '.', DecimalSeparator, []);
                {$IFDEF VCL4}
                if Field is TLargeIntField then
                  (Field as TLargeIntField).AsLargeInt := Trunc(StrToFloat(tmpStr))
                else {$ENDIF}
                  Field.AsInteger := Trunc(StrToFloat(tmpStr));
              end
              else
                Field.AsVariant := NULL;
            except
              if FFormats.FBooleanTrue.IndexOf(Str) > -1 then
                Field.AsInteger := 1
              else if FFormats.FBooleanFalse.IndexOf(Str) > -1 then
                Field.AsInteger := 0
              else
                raise;
            end;
          end;
        iftBoolean: Field.AsBoolean := StringToBoolean(Str);
        iftDouble:
          begin
            sTmp := StringReplace(Str, '.', DecimalSeparator, []);
            if (AnsiUpperCase(sTmp) <> 'NULL') and (Trim(STmp) <> '') then  //igorp
              Field.AsFloat := StrToFloat(GetNumericString(sTmp))
            else
              Field.AsVariant := NULL; //|igorp
          end;
        iftCurrency: Field.AsCurrency := StrToCurr(GetNumericString(Str));
        iftDateTime:
          begin
            if Str <> EmptyStr then //igorp
            begin
              if TryStrToDateTime(Str, DT) then
                Field.AsDateTime := DT
              else begin
                Field.AsVariant := NULL;
                raise Exception.CreateFmt({$IFDEF WIN32}QImportLoadStr(QIE_WrongDateFormat){$ENDIF}
                                          {$IFDEF LINUX}QIE_WrongDateFormat{$ENDIF}, [Str]);
              end;
            end
            else
              Field.AsVariant := NULL;  //|igorp
          end;
        else
          raise Exception.Create({$IFDEF WIN32}QImportLoadStr(QIE_UnknownFieldType){$ENDIF}
                                 {$IFDEF LINUX}QIE_UnknownFieldType{$ENDIF});
      end;
    end;
  except
    on E: Exception do
      Result := E.Message;
  end;
end;

function TQImport2.DestinationFindByFields: Boolean;
var
  i{$IFNDEF NOGUI}, j{$ENDIF}: Integer;
  fieldNames: string;
  values: Variant;
  col: TQImportCol;
  {$IFNDEF NOGUI}flag: Boolean; {$ENDIF}
begin
  Result := False;

  if FAllowDuplicates then Exit;

  if FIsCSV then Exit;
  case ImportDestination of
    qidDataSet:
      if FMappedColumns.Count > 0 then
      begin
        fieldNames := EmptyStr;
        if FMappedColumns.Count > 1 then
          values := VarArrayCreate([0, FMappedColumns.Count - 1], varVariant);
        for i := 0 to FMappedColumns.Count - 1 do
        begin
          col := FImportRow.ColByName(FMappedColumns[i]);
          if Assigned(col) then
          begin
            fieldNames := fieldNames + col.Name;
            if FMappedColumns.Count > 1 then
              Values[i] := col.Value
            else
              Values := col.Value;
          end;
          if i < FMappedColumns.Count - 1 then
            fieldNames := fieldNames + ';';
        end;
        try
          Result := DataSet.Locate(fieldNames, values, [])
        except
          Result := False;
        end;
      end;
    {$IFNDEF NOGUI}
    qidDBGrid:
      if FMappedColumns.Count > 0 then
      begin
        fieldNames := EmptyStr;
        if FMappedColumns.Count > 1 then
          values := VarArrayCreate([0, FMappedColumns.Count - 1], varVariant);
        for i := 0 to FMappedColumns.Count - 1 do
        begin
          col := FImportRow.ColByName(FMappedColumns[i]);
          if Assigned(col) then
          begin
            fieldNames := fieldNames + DBGrid.Columns[col.FColumnIndex].Field.FieldName;
            if FMappedColumns.Count > 1 then
              Values[i] := col.Value
            else
              Values := col.Value;
          end;
          if i < FMappedColumns.Count - 1 then
            fieldNames := fieldNames + ';';
        end;
        Result := DBGrid.DataSource.DataSet.Locate(fieldNames, values, []);
      end;
    qidListView:
      for i := 0 to ListView.Items.Count - 1 do
      begin
        flag := False;
        for j := 0 to FMappedColumns.Count - 1 do
        begin
          col := FImportRow.ColByName(FMappedColumns[j]);
          if Assigned(col) then
          begin
            if col.FColumnIndex = 0 then
              flag := AnsiCompareStr(col.Value, ListView.Items[i].Caption) = 0
            else
              flag := AnsiCompareStr(col.Value, ListView.Items[i].SubItems[col.FColumnIndex - 1]) = 0;
            if not flag then
              Break;
          end
          else
            Exit;
        end;
        if flag then
        begin
          Result := True;
          FCurrListItem := ListView.Items[i];
          Exit;
        end;
      end;
    qidStringGrid:
      for i := 0 to StringGrid.RowCount - 1 do
      begin
        if (GridCaptionRow > -1) and (i = GridCaptionRow) then
          Continue;
        Flag := False;
        for j := 0 to FMappedColumns.Count - 1 do
        begin
          col := FImportRow.ColByName(FMappedColumns[j]);
          if Assigned(col) then
            flag := AnsiCompareStr(col.Value, StringGrid.Cells[col.FColumnIndex, i]) = 0
          else
            Exit;
          if not flag then
            Break;
        end;
        if flag then
        begin
          Result := True;
          FCurrStrGrRow := i;
          Exit;
        end;
      end;
    {$ENDIF}
  end;
end;

{ TQImportCol }

constructor TQImportCol.Create;
begin
  inherited;
  FColumnIndex := -1;
  FName := EmptyStr;
  FValue := EmptyStr;
  FIsBinary := false;
end;

{ TQImportRow }

constructor TQImportRow.Create(AImport: TQImport2);
begin
  inherited Create;
  FIndex := TStringList.Create;
  FQImport := AImport;
end;

destructor TQImportRow.Destroy;
begin
  Clear;
  FIndex.Free;
  inherited;
end;

function TQImportRow.Add(const AName: string): TQImportCol;
begin
  Result := TQImportCol.Create;
  Result.Name := Trim(AName);
  inherited Add(Result);
end;

procedure TQImportRow.Clear;
var
  i: integer;
begin
  for i := Count - 1 downto 0 do Delete(i);
  inherited;
end;

procedure TQImportRow.Delete(Index: integer);
begin
  TQImportCol(Items[Index]).Free;
  inherited Delete(Index);
end;

function TQImportRow.First: TQImportCol;
begin
  Result := TQImportCol(inherited First);
end;

procedure TQImportRow.Insert(Index: Integer; Item: TQImportCol);
begin
  inherited Insert(Index, Item);
end;

procedure TQImportRow.SetValue(const AName, AValue: string; IsBinary: boolean);
var
  i: integer;
begin
  FIndex.Find(AName, i);
  if i > -1 then begin
    i := Integer(FIndex.Objects[i]);
    if IsBinary then begin
      Items[i].Value := AValue;
      Items[i].FIsBinary := true;
    end
    else Items[i].Value := Trim(AValue);
  end;
end;

procedure TQImportRow.ClearValues;
var
  i: integer;
begin
  for i := 0 to Count - 1 do
    Items[i].Value := EmptyStr
end;

function TQImportRow.Last: TQImportCol;
begin
  Result := TQImportCol(inherited Last);
end;

function TQImportRow.IndexOf(Item: TQImportCol): Integer;
begin
  Result := inherited IndexOf(Item);
end;

function TQImportRow.Remove(Item: TQImportCol): Integer;
begin
  Result := inherited Remove(Item);
end;

function TQImportRow.ColByName(const AName: string): TQImportCol;
var
  i: integer;
begin
  Result := nil;
  for i := 0 to Count - 1 do
    if AnsiCompareText(AName, Items[i].Name) = 0 then begin
      Result := Items[i];
      Exit;
    end;
end;

function TQImportRow.Get(Index: Integer): TQImportCol;
begin
  Result := TQImportCol(inherited Get(Index));
end;

procedure TQImportRow.Put(Index: Integer; const Value: TQImportCol);
begin
  inherited Put(Index, Value);
end;

{ TQImportReplacement }

constructor TQImportReplacement.Create(Collection: TCollection);
begin
  inherited;
  FIgnoreCase := false;
end;

procedure TQImportReplacement.Assign(Source: TPersistent);
begin
  if Source is TQImportReplacement then begin
    TextToFind := (Source as TQImportReplacement).TextToFind;
    ReplaceWith := (Source as TQImportReplacement).ReplaceWith;
    IgnoreCase := (Source as TQImportReplacement).IgnoreCase;
    Exit;
  end;
  inherited;
end;

function TQImportReplacement.GetDisplayName: string;
begin
  Result := Format('%s - %s', [FTextToFind, FReplaceWith]);
end;

{ TQImportReplacements }

constructor TQImportReplacements.Create(Holder: TPersistent);
begin
  inherited Create(TQImportReplacement);
  FHolder := Holder;
end;

function TQImportReplacements.Add: TQImportReplacement;
begin
  Result := (inherited Add) as TQImportReplacement;
end;

function TQImportReplacements.GetOwner: TPersistent;
begin
  Result := FHolder;
end;

function TQImportReplacements.GetItem(Index: integer): TQImportReplacement;
begin
  Result := inherited Items[Index] as TQImportReplacement;
end;

procedure TQImportReplacements.SetItem(Index: integer;
  Replacement: TQImportReplacement);
begin
  inherited Items[Index] := Replacement;
end;

function TQImportReplacements.ItemExists(const ATextToFind, AReplaceWith: string;
  AIgnoreCase: Boolean): Boolean;
var
  i: Integer;
begin
  Result := False;
  for i := 0 to Count - 1 do
  begin
    if Items[i] <> nil then
    begin
      Result := (AnsiCompareStr(Items[i].TextToFind, ATextToFind) = 0) and
        (AnsiCompareStr(Items[i].ReplaceWith, AReplaceWith) = 0) and
        (Items[i].IgnoreCase = AIgnoreCase);
      if Result then
        Break;
    end;
  end;
end;

{ TQImportGenerators }

destructor TQImportGenerators.Destroy;
var
  i: integer;
begin
  for i := Count - 1 downto 0 do Delete(i);
  inherited;
end;

function TQImportGenerators.Add(const AName: string; AValue, AStep: integer): TQImportGenerator;
begin
  Result := TQImportGenerator.Create;
  Result.Name := Trim(AName);
  Result.Value := AValue;
  Result.Step := AStep;
  inherited Add(Result);
end;

procedure TQImportGenerators.Delete(Index: integer);
begin
  TQImportGenerator(Items[Index]).Free;
  inherited Delete(Index);
end;

function TQImportGenerators.Get(Index: Integer): TQImportGenerator;
begin
  Result := TQImportGenerator(inherited Get(Index));
end;

function TQImportGenerators.GetNewValue(const AName: string): integer;
var
  i: integer;
begin
  Result := 0;
  for i := 0 to Count - 1 do
    if AnsiCompareText(Items[i].Name, Trim(AName)) = 0 then begin
      Result := Items[i].GetNewValue;
      Exit;
    end;
end;

procedure TQImportGenerators.Put(Index: Integer; const Value: TQImportGenerator);
begin
  inherited Put(Index, Value);
end;

function TQImportGenerators.GenByName(const AName: string): TQImportGenerator;
var
  i: integer;
begin
  Result := nil;
  for i := 0 to Count - 1 do
    if AnsiCompareText(Items[i].Name, Trim(AName)) = 0 then begin
      Result := Items[i];
      Exit;
    end;
end;

{ TQImportGenerator }

function TQImportGenerator.GetNewValue: integer;
begin
  Inc(FValue, FStep);
  Result := FValue;
end;

{ TQImportFieldFormat }

constructor TQImportFieldFormat.Create(Collection: TCollection);
begin
  inherited;
  FGeneratorValue := 0;
  FGeneratorStep := 0;
  FConstantValue := EmptyStr;
  FNullValue := EmptyStr;
  FDefaultValue := EmptyStr;
  FLeftQuote := EmptyStr;
  FRightQuote := EmptyStr;
  FQuoteAction := qaNone;
  FCharCase := iccNone;
  FCharSet := icsNone;
  FReplacements := TQImportReplacements.Create(Self);
end;

destructor TQImportFieldFormat.Destroy;
begin
  FReplacements.Free;
  inherited;
end;

function TQImportFieldFormat.GetDisplayName: string;
begin
  Result := inherited GetDisplayName;
end;

function TQImportFieldFormat.IsConstant: boolean;
begin
  Result := FConstantValue <> EmptyStr;
end;

function TQImportFieldFormat.IsNull: boolean;
begin
  Result := FNullValue <> EmptyStr;
end;

function TQImportFieldFormat.IsDefault: boolean;
begin
  Result := FDefaultValue <> EmptyStr;
end;

function TQImportFieldFormat.IsLeftQuote: boolean;
begin
  Result := FLeftQuote <> EmptyStr;
end;

function TQImportFieldFormat.IsRightQuote: boolean;
begin
  Result := FRightQuote <> EmptyStr;
end;

procedure TQImportFieldFormat.Assign(Source: TPersistent);
begin
  if Source is TQImportFieldFormat then begin
    FieldName := (Source as TQImportFieldFormat).FieldName;
    GeneratorValue := (Source as TQImportFieldFormat).GeneratorValue;
    GeneratorStep := (Source as TQImportFieldFormat).GeneratorStep;
    ConstantValue := (Source as TQImportFieldFormat).ConstantValue;
    NullValue := (Source as TQImportFieldFormat).NullValue;
    DefaultValue := (Source as TQImportFieldFormat).DefaultValue;
    LeftQuote := (Source as TQImportFieldFormat).LeftQuote;
    RightQuote := (Source as TQImportFieldFormat).RightQuote;
    QuoteAction := (Source as TQImportFieldFormat).QuoteAction;
    CharCase := (Source as TQImportFieldFormat).CharCase;
    CharSet := (Source as TQImportFieldFormat).CharSet;
    Replacements := (Source as TQImportFieldFormat).Replacements;
    Exit;
  end;
  inherited;
end;

function TQImportFieldFormat.IsDefaultValues: boolean;
begin
  Result := (GeneratorValue = 0) and
            (GeneratorStep = 0) and
            (ConstantValue = EmptyStr) and
            (NullValue = EmptyStr) and
            (DefaultValue = EmptyStr) and
            (LeftQuote = EmptyStr) and
            (RightQuote = EmptyStr) and
            (QuoteAction = qaNone) and
            (CharCase = iccNone) and
            (CharSet = icsNone) and
            (Replacements.Count = 0);
end;

procedure TQImportFieldFormat.SetReplacements(const Value: TQImportReplacements);
begin
  FReplacements.Assign(Value);
end;

{ TQImportFieldFormats }

constructor TQImportFieldFormats.Create(AHolder: TComponent);
begin
  inherited Create(TQImportFieldFormat);
  FHolder := AHolder;
end;

function TQImportFieldFormats.Add: TQImportFieldFormat;
begin
  Result := TQImportFieldFormat(inherited Add);
end;

function TQImportFieldFormats.GetItem(Index: integer): TQImportFieldFormat;
begin
  Result := TQImportFieldFormat(inherited Items[Index]);
end;

function TQImportFieldFormats.GetOwner: TPersistent;
begin
  Result := FHolder;
end;

procedure TQImportFieldFormats.SetItem(Index: integer; FieldFormat: TQImportFieldFormat);
begin
  inherited SetItem(Index, TCollectionItem(FieldFormat));
end;

function TQImportFieldFormats.IndexByName(const FieldName: string): integer;
var
  i: integer;
begin
  Result := -1;
  for i := 0 to Count - 1 do
    if AnsiCompareText(FieldName, Items[i].FieldName) = 0 then begin
      Result := i;
      Exit;
    end;
end;

{ TQImportLocale }

{$IFDEF WIN32}
constructor TQImportLocale.Create;
begin
  FIDEMode := AnsiUpperCase(ExtractFileName(ParamStr(0))) = 'DELPHI32.EXE';
end;

procedure TQImportLocale.LoadDll(const Name: string);
begin
  if FLoaded then
    UnloadDll;

  FDllHandle := LoadLibrary(PChar(Name));
  FLoaded := FDllHandle <> HINSTANCE_ERROR;

  { TODO : Perhaps, any localization code will be needed here }
end;

function TQImportLocale.LoadStr(ID: Integer): string;
var
  Buffer: array[0..1023] of Char;
  Handle: THandle;
begin
  if Assigned(FOnLocalize) then
  begin
    Result := '';
    FOnLocalize(ID, Result);
    if Result <> '' then
      Exit;
  end;

  if FLoaded then
    Handle := FDllHandle
  else
    Handle := HInstance;

  if FIDEMode then
    Result := SysUtils.LoadStr(ID)
  else
    SetString(Result, Buffer, LoadString(Handle, ID, Buffer, SizeOf(Buffer)));
end;

procedure TQImportLocale.UnloadDll;
begin
  if FLoaded then
    FreeLibrary(FDllHandle);

  FLoaded := False;

  { TODO : Perhaps, any localization code will be needed here }
end;
{$ENDIF}

initialization

finalization
{$IFDEF WIN32}
  Locale.Free;
  Locale := nil;
{$ENDIF}  

end.
